/****************************************************************************
Unicode符号范围 | UTF-8编码方式
    (十六进制) | （二进制)
0000 0000-0000 007F:0xxxxxxx
0000 0080-0000 07FF:110xxxxx 10xxxxxx
0000 0800-0000 FFFF:1110xxxx 10xxxxxx 10xxxxxx
0001 0000-001F FFFF:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
0020 0000-03FF FFFF:111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
0400 0000-7FFF FFFF:1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
**************************************************************************/
#include "functions.h"

unsigned char utf8_look_for_table[] =
        {
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
                2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
                3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
                4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1};

#define UTFLEN(x) utf8_look_for_table[(x)]

//根据首字节,获取utf8字符所占字节数
inline int get_utf8char_byte_num(unsigned char ch) {
    int byteNum = 0;

    if (ch >= 0xFC && ch < 0xFE)
        byteNum = 6;
    else if (ch >= 0xF8)
        byteNum = 5;
    else if (ch >= 0xF0)
        byteNum = 4;
    else if (ch >= 0xE0)
        byteNum = 3;
    else if (ch >= 0xC0)
        byteNum = 2;
    else if (0 == (ch & 0x80))
        byteNum = 1;

    return byteNum;
}

//判断字符串是否是utf8格式
int is_utf8_format(const char *str) {
    int byteNum = 0;
    unsigned char ch;
    const char *ptr = str;

    if (NULL == str) {
        return 0;
    }

    while (*ptr != '\0') {
        ch = (unsigned char) *ptr;
        if (byteNum == 0) //根据首字节特性判断该字符的字节数
        {
            if (0 == (byteNum = get_utf8char_byte_num(ch)))
                return 0;
        } else //多字节字符,非首字节格式:10xxxxxx
        {
            if ((ch & 0xC0) != 0x80)
                return 0;
        }
        byteNum--;
        ptr++;
    }

    if (byteNum > 0)
        return 0;

    return 1;
}

//计算utf8字符串字符个数
int get_utf8_length(char *str) {
    int clen = 0;
    int len = 0;
    int byteNum = 0;
    unsigned char ch;
    char *ptr = str;

    if (NULL == str)
        return 0;

    clen = strlen(str);
    while (*ptr != '\0' && len < clen) {
        ch = (unsigned char) *ptr;
        if (0 == (byteNum = get_utf8char_byte_num(ch)))
            return 0;
        ptr += byteNum;
        len++;
    }

    return len;
}

// 编码转换
int code_convert(char *from_charset, char *to_charset, char *inbuf, size_t inlen, char *outbuf, size_t outlen) {

    iconv_t cd;
    char **pin = &inbuf;
    char **pout = &outbuf;
    int conv_res = 0;
    cd = iconv_open(to_charset, from_charset);
    if (cd == 0)
        return -1;
    memset(outbuf, 0, outlen);
    conv_res = iconv(cd, pin, &inlen, pout, &outlen);
    // 返回值 conv_res 可能的值：
    // 0 （无错误）； 7：E2BIG；  84：EILSEQ； 22：EINVAL
    // 注意：outlen 的长度必须大于 inlen 的长度，否则会容易报错
    if (conv_res == -1) {
        iconv_close(cd);
        return -1;
    }
    iconv_close(cd);
    *pout = '\0';
    return 0;
}

int utf8_to_gbk(char *inbuf, size_t inlen, char *outbuf, size_t outlen) {
    return code_convert("utf-8", "GB18030", inbuf, inlen, outbuf, outlen);
}

int gbk_to_utf8(char *inbuf, size_t inlen, char *outbuf, size_t outlen) {
    return code_convert("GB18030", "utf-8", inbuf, inlen, outbuf, outlen);
}